﻿using System;
using System.Collections.Generic;
using System.Collections.Specialized;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using System.IO;
//using NAudio.Wave;
using System.Media;

namespace SoundRSEditor
{
    public partial class Form1 : Form
    {
        OrderedDictionary sounds;
        //IWavePlayer waveOut;
        string programCaption = "Dune 2000 Sound Effect Tool";
        public Form1()
        {
            InitializeComponent();
            this.Text = programCaption;
        }

        private void openButton_Click(object sender, EventArgs e)
        {
            // Let the user pick the files to parse
            OpenFileDialog dialog = new OpenFileDialog();
            dialog.Filter = "Dune 2000 RS files (*.RS)|*.RS";
            //dlg.Multiselect = true;
            if (dialog.ShowDialog() == DialogResult.OK)
            {
                ParseRS(dialog.FileName);
            }
        }
        string ReadLine(BinaryReader br)
        {
            string line = "";

            char readChar = br.ReadChar();

            while(readChar != '\0')
            {
                line += readChar;
                readChar = br.ReadChar();                
            }

            return line;
        }
        void ParseRS(string filename)
        {
            this.Text = Path.GetFileNameWithoutExtension(filename) + " - " + programCaption;
            sounds = new OrderedDictionary();
            listBox1.Items.Clear();
            
            FileStream fs = new FileStream(filename, FileMode.Open);
            BinaryReader br = new BinaryReader(fs);

            UInt32 headerLength = br.ReadUInt32();

            while (fs.Position < headerLength + 4)
            {
                string name = ReadLine(br);
                
                UInt32 offset = br.ReadUInt32();
                UInt32 size = br.ReadUInt32();
                sounds.Add(name, new SoundEffect(offset, size));
            }
            byte[] data = br.ReadBytes((int)fs.Length - (int)fs.Position);
            br.Close();
            fs.Close();

            String[] keys = new String[sounds.Count];
            SoundEffect[] values = new SoundEffect[sounds.Count];
            sounds.Keys.CopyTo(keys, 0);
            sounds.Values.CopyTo(values, 0);

            for (int i = 0; i < keys.Length; i++)
            {
                values[i].Data = new byte[values[i].Size];
                Array.Copy(data, (int)values[i].Offset - ((int)headerLength + 4), values[i].Data, 0, values[i].Size);
                listBox1.Items.Add(keys[i] + "\t\t\t\t" +
                    ((values[i].AudioFormat == 1) ? "PCM" : "Compressed")
                    + " Channels: " + values[i].NumChannels + " Sample Rate: " + values[i].SampleRate + " Hz");
            }

            //fs = new FileStream("test.wav", FileMode.CreateNew);
            //BinaryWriter bw = new BinaryWriter(fs);
            //bw.Write(sounds["THUMPER1.WAV"].Data);
            //bw.Close();
            //fs.Close();


            //MemoryStream ms = new MemoryStream();
            //ms.Write(sounds["THUMPER1.WAV"].Data, 0, (int)sounds["THUMPER1.WAV"].Size);
            //System.Media.SoundPlayer myPlayer = new System.Media.SoundPlayer();
            //myPlayer.Stream = ms;
            //myPlayer.Play();
            //ms.Close();
        }

        void SaveRS(string filename)
        {
            FileStream fs = new FileStream(filename, FileMode.Create);
            BinaryWriter bw = new BinaryWriter(fs);
            
            // Calculate header length
            UInt32 headerLength = CalculateHeaderLength();
            
            // Write header length
            bw.Write(headerLength);

            UInt32 position = headerLength + 4; // Include header length variable

            String[] keys = new String[sounds.Count];
            SoundEffect[] values = new SoundEffect[sounds.Count];
            sounds.Keys.CopyTo(keys, 0);
            sounds.Values.CopyTo(values, 0);

            // Write header
            for (int i = 0; i < keys.Length; i++)
            {
                // Name                
                bw.Write(keys[i].ToCharArray());
                bw.Write('\0'); // Null terminator
                bw.Write(position);
                bw.Write(values[i].Size);
                position += values[i].Size;
            }

            // Write data
            for (int i = 0; i < keys.Length; i++)
            {
                bw.Write(values[i].Data);
            }

            bw.Close();
            fs.Close();
        }
        UInt32 CalculateHeaderLength()
        {
            // Header length is for each entry:
            // name length (including null terminator) + 8 bytes per entry
            UInt32 headerLength = 0;

            String[] keys = new String[sounds.Count];
            SoundEffect[] values = new SoundEffect[sounds.Count];
            sounds.Keys.CopyTo(keys, 0);
            sounds.Values.CopyTo(values, 0);

            // Write header
            for (int i = 0; i < keys.Length; i++)
            {
                // Name length + null terminator + offset variable length + size variable length
                headerLength += (UInt32)keys[i].Length + 1 + 4 + 4;
            }
            return headerLength;
        }

        private void saveButton_Click(object sender, EventArgs e)
        {
            if (sounds == null)
                return;

            SaveFileDialog dialog = new SaveFileDialog();
            dialog.Filter = "Dune 2000 RS files (*.RS)|*.RS";
            //dlg.Multiselect = true;
            if (dialog.ShowDialog() == DialogResult.OK)
            {
                SaveRS(dialog.FileName);
            }
        }

        private void replaceButton_Click(object sender, EventArgs e)
        {
            if (listBox1.SelectedIndex >= 0)
            {
                // Let the user pick the files to parse
                OpenFileDialog dialog = new OpenFileDialog();
                dialog.Filter = "Wav sounds (*.wav)|*.wav";
                //dlg.Multiselect = true;
                if (dialog.ShowDialog() == DialogResult.OK)
                {
                    ReplaceSound(listBox1.SelectedIndex, dialog.FileName);
                }
                

            }
        }

        private void ReplaceSound(int index, string filename)
        {
            FileStream fs = new FileStream(filename, FileMode.Open);
            BinaryReader br = new BinaryReader(fs);
            byte[] buffer = br.ReadBytes((int)fs.Length);
            br.Close();
            fs.Close();

            SoundEffect[] myValues = new SoundEffect[sounds.Count];
            sounds.Values.CopyTo(myValues, 0);

            myValues[index].Data = buffer;
            myValues[index].UpdateSize();

            if (buffer.Length > 65536)
            {
                MessageBox.Show("You have just replaced a sound with one with a file size greater than 64 KB (65536 bytes).\nBy default playing this sound will crash Dune.\nYou may want to replace it again with a smaller sound.");
            }
        }

        private void playButton_Click(object sender, EventArgs e)
        {
            if (listBox1.SelectedIndex >= 0)
            {
                MemoryStream ms = new MemoryStream();
                BinaryWriter bw = new BinaryWriter(ms);
                SoundEffect[] myValues = new SoundEffect[sounds.Count];
                sounds.Values.CopyTo(myValues, 0);

                bw.Write(myValues[listBox1.SelectedIndex].Data);
                ms.Position = 0;
                SoundPlayer soundPlayer = new SoundPlayer(ms);
                soundPlayer.Play();

                //WaveStream waveStream = new WaveFileReader(ms);

                //waveOut = new WaveOut();
                //waveOut.Init(waveStream);
                //waveOut.Play();

                bw.Close();
                ms.Close();
            }
        }

        private void importButton_Click(object sender, EventArgs e)
        {
            if (sounds != null)
            {
                // Let the user pick the files to parse
                OpenFileDialog dialog = new OpenFileDialog();
                dialog.Filter = "Wav sounds (*.wav)|*.wav";
                //dlg.Multiselect = true;
                if (dialog.ShowDialog() == DialogResult.OK)
                {
                    ImportSound(dialog.FileName);
                }
            }
        }

        private void ImportSound(string filename)
        {
            string trimmedFilename = Path.GetFileName(filename).ToUpper();

            string[] keys = new string[sounds.Count];
            sounds.Keys.CopyTo(keys, 0);
            List<string> list = new List<string>(keys);

            if(!list.Contains(trimmedFilename))
            {
                FileStream fs = new FileStream(filename, FileMode.Open);
                BinaryReader br = new BinaryReader(fs);
                byte[] buffer = br.ReadBytes((int)fs.Length);
                br.Close();
                fs.Close();

                SoundEffect soundEffect = new SoundEffect(0, (uint)buffer.Length);
                soundEffect.Data = buffer;

                sounds.Add(trimmedFilename, soundEffect);

                listBox1.Items.Add(trimmedFilename + "\t\t\t\t" +
                    ((soundEffect.AudioFormat == 1) ? "PCM" : "Compressed")
                    + " Channels: " + soundEffect.NumChannels + " Sample Rate: " + soundEffect.SampleRate + " Hz");

                if (buffer.Length > 65536)
                {
                    MessageBox.Show("You have just imported a sound with one with a file size greater than 64 KB (65536 bytes).\nBy default playing this sound will crash Dune.\nYou may want to replace it with a smaller sound.");
                }
            }
            else
            {
                MessageBox.Show("Sound already exists, try using replace or delete instead");
            }

        }

        private void deleteButton_Click(object sender, EventArgs e)
        {
            if (listBox1.SelectedIndex >= 0)
            {
                string[] keys = new string[sounds.Count];
                sounds.Keys.CopyTo(keys, 0);
                sounds.Remove(keys[listBox1.SelectedIndex]);
                listBox1.Items.RemoveAt(listBox1.SelectedIndex);
            }
        }

        private void exportButton_Click(object sender, EventArgs e)
        {
            if (sounds == null || listBox1.SelectedIndex < 0)
                return;

            SaveFileDialog dialog = new SaveFileDialog();
            dialog.Filter = "Wave sounds (*.wav)|*.wav";
            string[] keys = new string[sounds.Count];
            sounds.Keys.CopyTo(keys, 0);
            dialog.FileName = keys[listBox1.SelectedIndex];

            //dlg.Multiselect = true;
            if (dialog.ShowDialog() == DialogResult.OK)
            {
                SoundEffect[] myValues = new SoundEffect[sounds.Count];
                sounds.Values.CopyTo(myValues, 0);

                SaveWav(dialog.FileName, myValues[listBox1.SelectedIndex]);
            }
        }
        void SaveWav(string filename, SoundEffect sound)
        {
            FileStream fs = new FileStream(filename, FileMode.Create);
            BinaryWriter bw = new BinaryWriter(fs);

            bw.Write(sound.Data);

            bw.Close();
            fs.Close();
        }

        private void toolStripStatusLabel2_Click(object sender, EventArgs e)
        {
            System.Diagnostics.Process.Start("http://mvinetwork.co.uk/dune");
        }
    }
}
